package encrypt

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
)

// EncryptCBC CBC加密
func EncryptCBC(key, plaintext string) (ciphertext string, err error) {
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return
	}
	plainText := []byte(plaintext)
	blockSize := block.BlockSize()
	padding := blockSize - len(plainText)%blockSize
	plainText = append(plainText, bytes.Repeat([]byte{byte(padding)}, padding)...)
	cipherText := make([]byte, blockSize+len(plainText))
	iv := cipherText[:blockSize]
	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
		return
	}
	mode := cipher.NewCBCEncrypter(block, iv)
	mode.CryptBlocks(cipherText[blockSize:], plainText)
	ciphertext = base64.URLEncoding.EncodeToString(cipherText)
	return
}

// DecryptCBC CBC解密
func DecryptCBC(key, ciphertext string) (plaintext string, err error) {
	cipherText, err := base64.URLEncoding.DecodeString(ciphertext)
	if err != nil {
		return
	}
	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return
	}
	blockSize := block.BlockSize()
	if len(cipherText) < blockSize {
		err = errors.New("ciphertext too short")
		return
	}
	if len(cipherText)%blockSize != 0 {
		err = errors.New("ciphertext is not not full blocks")
		return
	}
	iv := cipherText[:blockSize]
	cipherText = cipherText[blockSize:]
	mode := cipher.NewCBCDecrypter(block, iv)
	mode.CryptBlocks(cipherText, cipherText)
	length := len(cipherText)
	plaintext = string(cipherText[:(length - int(cipherText[length-1]))])
	return
}
